/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package io.netty.buffer;

import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
import static java.lang.Math.max;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import io.netty.util.internal.LongCounter;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;

/**PoolArena是由多个PoolChunk组成的大块内存区域，而每个PoolChunk则由多个Page组成。
 * <br/>当需要分配的内存小于Page的时候，为了节约内存采用PoolSubpage实现小于Page大小内存的分配。
 * <br/>在PoolArena中为了保证PoolChunk空间的最大利用化，按照PoolArena中各个PoolChunk已使用的空间大小将其划分为六类：
 * <br/>qInit：存储内存利用率 0-25% 的 chunk
 * <br/>q000：存储内存利用率 1-50% 的 chunk
 * <br/>q025：存储内存利用率 25-75% 的 chunk
 * <br/>q050：存储内存利用率 50-100% 的 chunk
 * <br/>q075：存储内存利用率 75-100%的 chunk
 * <br/>q100：存储内存利用率 100%的 chunk
 * <br/>PoolArena维护了一个PoolChunkList组成的双向链表，每个PoolChunkList内部维护了一个PoolChunk双向链表。
 * <br/>分配内存时，PoolArena通过在PoolChunkList找到一个合适的PoolChunk，然后从PoolChunk中分配一块内存。
 * @author Ziroom
 *
 * @param <T>
 */
abstract class PoolArena<T> implements PoolArenaMetric {
    static final boolean HAS_UNSAFE = PlatformDependent.hasUnsafe();

    enum SizeClass {
        Tiny,
        Small,
        Normal
    }

    /**该参数指定了tinySubpagePools数组的长度，由于tinySubpagePools每一个元素的内存块差值为16， 因而数组长度是512/16，也即这里的512 >>> 4
     *
     */
    static final int numTinySubpagePools = 512 >>> 4;

    /**表示该PoolArena的allocator
     *
     */
    final PooledByteBufAllocator parent;

    /**表示PoolChunk中由Page节点构成的二叉树的最大高度，默认11
     *
     */
    private final int maxOrder;
    /**page的大小，默认8K
     *
     */
    final int pageSize;
    /**指定了叶节点大小8KB是2的多少次幂，默认为13，该字段的主要作用是，在计算目标内存属于二叉树的第几层的时候，可以借助于其内存大小相对于pageShifts的差值，从而快速计算其所在层数
     *
     */
    final int pageShifts;
    /**默认16MB
     *
     */
    final int chunkSize;
    /**由于PoolSubpage的大小为8KB=8196，因而该字段的值为-8192=>=> 1111 1111 1111 1111 1110 0000 0000 0000
     * <br/>这样在判断目标内存是否小于8KB时，只需要将目标内存与该数字进行与操作，只要操作结果等于0，就说明目标内存是小于8KB的，
     * <br/>这样就可以判断其是应该首先在tinySubpagePools或smallSubpagePools中进行内存申请
     *
     */
    final int subpageOverflowMask;
    /**该参数指定了smallSubpagePools数组的长度，默认为4
     *
     */
    final int numSmallSubpagePools;
    /**是否开启直接内存缓存对齐
     *
     */
    final int directMemoryCacheAlignment;
    final int directMemoryCacheAlignmentMask;
    /**tinySubpagePools用来分配小于512 byte的Page
     *
     */
    private final PoolSubpage<T>[] tinySubpagePools;
    /**smallSubpagePools用来分配大于等于512 byte且小于pageSize内存的Page
     *
     */
    private final PoolSubpage<T>[] smallSubpagePools;

    // 用来存储用来分配给大于等于pageSize大小内存的PoolChunk
    /**存储内存利用率50-100%的chunk
     *
     */
    private final PoolChunkList<T> q050;
    /**存储内存利用率25-75%的chunk
     *
     */
    private final PoolChunkList<T> q025;
    /**存储内存利用率1-50%的chunk
     *
     */
    private final PoolChunkList<T> q000;
    /**存储内存利用率0-25%的chunk
     *
     */
    private final PoolChunkList<T> qInit;
    /**存储内存利用率75-100%的chunk
     *
     */
    private final PoolChunkList<T> q075;
    /**存储内存利用率100%的chunk
     *
     */
    private final PoolChunkList<T> q100;

    private final List<PoolChunkListMetric> chunkListMetrics;

    // Metrics for allocations and deallocations
    private long allocationsNormal;
    // We need to use the LongCounter here as this is not guarded via synchronized block.
    private final LongCounter allocationsTiny = PlatformDependent.newLongCounter();
    private final LongCounter allocationsSmall = PlatformDependent.newLongCounter();
    private final LongCounter allocationsHuge = PlatformDependent.newLongCounter();
    private final LongCounter activeBytesHuge = PlatformDependent.newLongCounter();

    private long deallocationsTiny;
    private long deallocationsSmall;
    private long deallocationsNormal;

    // We need to use the LongCounter here as this is not guarded via synchronized block.
    private final LongCounter deallocationsHuge = PlatformDependent.newLongCounter();

    // Number of thread caches backed by this arena.
    final AtomicInteger numThreadCaches = new AtomicInteger();

    // TODO: Test if adding padding helps under contention
    //private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;

    protected PoolArena(PooledByteBufAllocator parent, int pageSize, int maxOrder, int pageShifts, int chunkSize, int cacheAlignment) {
        this.parent = parent;
        this.pageSize = pageSize;
        this.maxOrder = maxOrder;
        this.pageShifts = pageShifts;
        this.chunkSize = chunkSize;
        directMemoryCacheAlignment = cacheAlignment;
        directMemoryCacheAlignmentMask = cacheAlignment - 1;
        subpageOverflowMask = ~(pageSize - 1);
        tinySubpagePools = newSubpagePoolArray(numTinySubpagePools);
        for (int i = 0; i < tinySubpagePools.length; i ++) {
            tinySubpagePools[i] = newSubpagePoolHead(pageSize);
        }

        numSmallSubpagePools = pageShifts - 9;
        smallSubpagePools = newSubpagePoolArray(numSmallSubpagePools);
        for (int i = 0; i < smallSubpagePools.length; i ++) {
            smallSubpagePools[i] = newSubpagePoolHead(pageSize);
        }

        q100 = new PoolChunkList<T>(this, null, 100, Integer.MAX_VALUE, chunkSize);
        q075 = new PoolChunkList<T>(this, q100, 75, 100, chunkSize);
        q050 = new PoolChunkList<T>(this, q075, 50, 100, chunkSize);
        q025 = new PoolChunkList<T>(this, q050, 25, 75, chunkSize);
        q000 = new PoolChunkList<T>(this, q025, 1, 50, chunkSize);
        qInit = new PoolChunkList<T>(this, q000, Integer.MIN_VALUE, 25, chunkSize);

        q100.prevList(q075);
        q075.prevList(q050);
        q050.prevList(q025);
        q025.prevList(q000);
        q000.prevList(null);
        qInit.prevList(qInit);

        List<PoolChunkListMetric> metrics = new ArrayList<PoolChunkListMetric>(6);
        metrics.add(qInit);
        metrics.add(q000);
        metrics.add(q025);
        metrics.add(q050);
        metrics.add(q075);
        metrics.add(q100);
        chunkListMetrics = Collections.unmodifiableList(metrics);
    }

    private PoolSubpage<T> newSubpagePoolHead(int pageSize) {
        PoolSubpage<T> head = new PoolSubpage<T>(pageSize);
        head.prev = head;
        head.next = head;
        return head;
    }

    @SuppressWarnings("unchecked")
    private PoolSubpage<T>[] newSubpagePoolArray(int size) {
        return new PoolSubpage[size];
    }

    abstract boolean isDirect();

    PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
    	// 首先调用newByteBuf获得一个PooledByteBuf对象
        PooledByteBuf<T> buf = newByteBuf(maxCapacity);
        // 到这里，已经从回收站获取（或新建）了buf对象，接下来，看看真实的内存是如何分配出来的。buf内存分配有两种情况，分别是从缓存中进行内存分配和从内存堆里进行内存分配
        // 这里要注意, 这里进行分配的时候是线程私有的directArena进行分配
        allocate(cache, buf, reqCapacity);
        return buf;
    }

    /**这里直接将normCapacity除以16, 通过前面的内容我们知道, tiny类型缓存数组中每个元素规格化的数据都是16的倍数, 所以通过这种方式可以找到其下标, 如果是16B会拿到下标为1的元素, 如果是32B则会拿到下标为2的元素
     * @param normCapacity
     * @return
     */
    static int tinyIdx(int normCapacity) {
        return normCapacity >>> 4;
    }

    static int smallIdx(int normCapacity) {
        int tableIdx = 0;
        int i = normCapacity >>> 10;
        while (i != 0) {
            i >>>= 1;
            tableIdx ++;
        }
        return tableIdx;
    }

    // capacity < pageSize
    boolean isTinyOrSmall(int normCapacity) {
        return (normCapacity & subpageOverflowMask) == 0;
    }

    // normCapacity < 512
    static boolean isTiny(int normCapacity) {
        return (normCapacity & 0xFFFFFE00) == 0;
    }

    /**这里看起来逻辑比较长, 其实主要步骤分为两步
     * <br/>1. 首先在缓存上进行分配，对应步骤是:
     * <br/>cache.allocateTiny(this, buf, reqCapacity, normCapacity)
     * <br/>cache.allocateSmall(this, buf, reqCapacity, normCapacity)
     * <br/>cache.allocateNormal(this, buf, reqCapacity, normCapacity)
     * <br/>2.如果缓存分配不成功，则从PoolArena的tinySubpagePools或smallSubpagePools里分配
     * <br/>3. 如果还分配不成功，则实际分配一块内存，对应步骤是
     * <br/>allocateNormal(buf, reqCapacity, normCapacity)
     * <br/>在这里对几种类型的内存进行介绍:
     * <br/>之前的小节我们介绍过, 缓冲区内存类型分为tiny, small, 和normal, 其实还有种不常见的类型叫做huge, 那么这几种类型的内存有什么区别呢, 实际上这几种类型是按照缓冲区初始化空间的范围进行区分的, 具体区分如下:
     * <br/>tiny类型对应的缓冲区范围为0-512B
     * <br/>small类型对应的缓冲区范围为512B-8K
     * <br/>normal类型对应的缓冲区范围为8K-16MB
     * <br/>huge类型对应缓冲区范围为大于16MB
     * <br/>简单介绍下有关范围的含义:
     * <br/>16MB对应一个chunk, netty是以chunk为单位向操作系统申请内存的
     * <br/>8k对应一个page, page是将chunk切分后的结果, 一个chunk对应2048个page
     * <br/>8k以下对应一个subpage, subpage是page的切分, 一个page可以切分多个subpage, 具体切分几个需要根据subpage的大小而定, 比如只要分配1k的缓冲区, 则会将page切分成8个subpage
     * @param cache
     * @param buf
     * @param reqCapacity
     */
    private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
    	// 将需要申请的容量格式为 2^N
        final int normCapacity = normalizeCapacity(reqCapacity);
        // 判断目标容量是否小于8KB，小于8KB(page)则使用tiny或small的方式申请内存
        if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
            int tableIdx;
            PoolSubpage<T>[] table;
            boolean tiny = isTiny(normCapacity);
            // 判断是不是tiny
            if (tiny) { // < 512
            	// 如果是tiny, 则通过cache.allocateTiny(this, buf, reqCapacity, normCapacity)在缓存上进行分配
                if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                // 如果无法从当前线程缓存中申请到内存，则尝试从tinySubpagePools中申请，这里tinyIdx()方法
                // 就是计算目标内存是在tinySubpagePools数组中的第几号元素中
                tableIdx = tinyIdx(normCapacity);
                table = tinySubpagePools;
            } else {
            	// 如果目标内存在512byte~8KB之间，则尝试从smallSubpagePools中申请内存。这里首先从当前线程的缓存中申请small级别的内存，如果申请到了，则直接返回
                if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
                    // was able to allocate out of the cache so move on
                    return;
                }
                tableIdx = smallIdx(normCapacity);
                table = smallSubpagePools;
            }

            // 获取目标元素的头结点
            final PoolSubpage<T> head = table[tableIdx];

            /**
             * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
             * {@link PoolChunk#free(long)} may modify the doubly linked list as well.
             */
            // 这里需要注意的是，由于对head进行了加锁，而在同步代码块中判断了s != head， 也就是说PoolSubpage链表中是存在未使用的PoolSubpage的，因为如果该节点已经用完了，
            // 其是会被移除当前链表的。也就是说只要s != head，那么这里的allocate()方法 就一定能够申请到所需要的内存块
            synchronized (head) {
                final PoolSubpage<T> s = head.next;
                // 默认情况下, head的next也是自身，所以第一次访问时s == head
                // s != head就证明当前PoolSubpage链表中存在可用的PoolSubpage，并且一定能够申请到内存，因为已经耗尽的PoolSubpage是会从链表中移除的
                if (s != head) {
                	// 如果此时subpage已经被分配过内存了，则进入这里，如果只是初始化过（s == head），则跳过该分支
                    assert s.doNotDestroy && s.elemSize == normCapacity;
                    // 从PoolSubpage中申请内存
                    long handle = s.allocate();
                    assert handle >= 0;
                    // 通过申请的内存对ByteBuf进行初始化：初始化PoolByteBuf说明其位置被分配到该区域，但此时尚未分配内存
                    s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity);
                    // 对tiny类型或small类型的申请数进行更新
                    incTinySmallAllocation(tiny);
                    return;
                }
            }
            synchronized (this) {
            	// 走到这里，说明目标PoolSubpage链表中无法申请到目标内存块，因而就尝试从PoolChunk中申请
                allocateNormal(buf, reqCapacity, normCapacity);
            }

            incTinySmallAllocation(tiny);
            return;
        }
        // 走到这里说明目标内存是大于8KB的，那么就判断目标内存是否大于16M，如果大于16M， 则不使用内存池对其进行管理，如果小于16M，则到PoolChunkList中进行内存申请
        // Normal 级别分配的大小范围是 [4097B, 16M) 。核心思想是将 PoolChunk 拆分成 2048 个 page ，这是 Normal 分配的最小单位。每个 page 等大（pageSize=8KB），并在逻辑上通过一棵满二叉树管理这些 page 对象。我们申请的内存本质是组合若干个 page 。
        if (normCapacity <= chunkSize) {
        	// 小于16M，首先到当前线程的缓存中申请，如果申请到了则直接返回，如果没有申请到，则到PoolChunkList中进行申请
        	// 首先在缓存上进行内存分配
            if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
                // was able to allocate out of the cache so move on
            	// 分配成功, 返回
                return;
            }
            synchronized (this) {
            	// 分配不成功, 做实际的内存分配
                allocateNormal(buf, reqCapacity, normCapacity);
                ++allocationsNormal;
            }
        } else {
            // Huge allocations are never served via the cache so just call allocateHuge
        	// 对于大于16M的内存，Netty不会对其进行维护，而是直接申请，然后返回给用户使用
            allocateHuge(buf, reqCapacity);
        }
    }

    // Method must be called inside synchronized(this) { ... } block
    /**其实就是在Page级别上进行分配，分配一个或多个Page空间
     * @param buf
     * @param reqCapacity
     * @param normCapacity
     */
    private void allocateNormal(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
    	// 我们之前讲过, chunkList是存储不同内存使用量的chunk集合, 每个chunkList通过双向链表的形式进行关联, 这里的q050.allocate(buf, reqCapacity, normCapacity)就代表首先在q050这个chunkList上进行内存分配
    	// 首先将申请动作按照 q050→q025→q000→qInit→q075 的顺序依次交由各个 PoolChunkList 进行处理，如果在对应的 PoolChunkList 中申请到了内存，则直接返回，
	    // 第一次时都是空的，分配不了
        if (q050.allocate(buf, reqCapacity, normCapacity) || q025.allocate(buf, reqCapacity, normCapacity) ||
            q000.allocate(buf, reqCapacity, normCapacity) || qInit.allocate(buf, reqCapacity, normCapacity) ||
            q075.allocate(buf, reqCapacity, normCapacity)) {
        	// 如果在对应的PoolChunkList能申请到内存，则返回
            return;
        }

        // 如果申请不到，那么直接创建一个新的PoolChunk，然后在该PoolChunk中申请目标内存，最后将该PoolChunk添加到qInit中，
	    // 上面说过Chunk是Netty向操作系统申请内存块的最大单位，每个Chunk是16M，
	    // PoolChunk内部通过memoryMap数组维护了一颗完全平衡二叉树作为管理底层内存分布及回收的标记位，所有的子节点管理的内存也属于其父节点。
        PoolChunk<T> c = newChunk(pageSize, maxOrder, pageShifts, chunkSize);
        boolean success = c.allocate(buf, reqCapacity, normCapacity);
        assert success;
        qInit.add(c);
    }

    private void incTinySmallAllocation(boolean tiny) {
        if (tiny) {
            allocationsTiny.increment();
        } else {
            allocationsSmall.increment();
        }
    }

    private void allocateHuge(PooledByteBuf<T> buf, int reqCapacity) {
        PoolChunk<T> chunk = newUnpooledChunk(reqCapacity);
        activeBytesHuge.add(chunk.chunkSize());
        buf.initUnpooled(chunk, reqCapacity);
        allocationsHuge.increment();
    }

    void free(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, int normCapacity, PoolThreadCache cache) {
    	// 是否为unpooled
        if (chunk.unpooled) {
            int size = chunk.chunkSize();
            destroyChunk(chunk);
            activeBytesHuge.add(-size);
            deallocationsHuge.increment();
        } else {
        	// 哪种级别的size
            SizeClass sizeClass = sizeClass(normCapacity);
            if (cache != null && cache.add(this, chunk, nioBuffer, handle, normCapacity, sizeClass)) {
            	// 这里加到缓存后，如果成功，就会return，只是加入queue，并不会重置bitmap；如果不成功，就会调用freeChunk方法，这里会重置bitmap
                return;
            }

            // 将原先给ByteBuf分配的内存区段标记为未使用
            freeChunk(chunk, handle, sizeClass, nioBuffer, false);
        }
    }

    private SizeClass sizeClass(int normCapacity) {
        if (!isTinyOrSmall(normCapacity)) {
            return SizeClass.Normal;
        }
        return isTiny(normCapacity) ? SizeClass.Tiny : SizeClass.Small;
    }

    void freeChunk(PoolChunk<T> chunk, long handle, SizeClass sizeClass, ByteBuffer nioBuffer, boolean finalizer) {
        final boolean destroyChunk;
        synchronized (this) {
            // We only call this if freeChunk is not called because of the PoolThreadCache finalizer as otherwise this
            // may fail due lazy class-loading in for example tomcat.
            if (!finalizer) {
                switch (sizeClass) {
                    case Normal:
                        ++deallocationsNormal;
                        break;
                    case Small:
                        ++deallocationsSmall;
                        break;
                    case Tiny:
                        ++deallocationsTiny;
                        break;
                    default:
                        throw new Error();
                }
            }
            destroyChunk = !chunk.parent.free(chunk, handle, nioBuffer);
        }
        if (destroyChunk) {
            // destroyChunk not need to be called while holding the synchronized lock.
            destroyChunk(chunk);
        }
    }

    PoolSubpage<T> findSubpagePoolHead(int elemSize) {
        int tableIdx;
        PoolSubpage<T>[] table;
        if (isTiny(elemSize)) { // < 512
        	// 如果是tiny类型，就除以4得到tableIdx
            tableIdx = elemSize >>> 4;
            table = tinySubpagePools;
        } else {
            tableIdx = 0;
            elemSize >>>= 10;
            while (elemSize != 0) {
                elemSize >>>= 1;
                tableIdx ++;
            }
            table = smallSubpagePools;
        }

        return table[tableIdx];
    }

    /**从下面规格化逻辑看出, 这里将缓存大小规格化成固定大小, 确保每个缓存对象缓存的ByteBuf容量统一
     * @param reqCapacity
     * @return
     */
    int normalizeCapacity(int reqCapacity) {
        checkPositiveOrZero(reqCapacity, "reqCapacity");

        // 如果大于16m，直接返回reqCapacity
        if (reqCapacity >= chunkSize) {
            return directMemoryCacheAlignment == 0 ? reqCapacity : alignCapacity(reqCapacity);
        }

        // 如果大于等于tiny类型的大小, 也就是512, 则会找一个大于并且最接近reqCapacity的2的次幂的数值
        if (!isTiny(reqCapacity)) { // >= 512
        	// 找一个2的幂次方的数值, 确保数值大于等于reqCapacity
            // Doubled
            int normalizedCapacity = reqCapacity;
            normalizedCapacity --;
            normalizedCapacity |= normalizedCapacity >>>  1;
            normalizedCapacity |= normalizedCapacity >>>  2;
            normalizedCapacity |= normalizedCapacity >>>  4;
            normalizedCapacity |= normalizedCapacity >>>  8;
            normalizedCapacity |= normalizedCapacity >>> 16;
            normalizedCapacity ++;

            if (normalizedCapacity < 0) {
                normalizedCapacity >>>= 1;
            }
            assert directMemoryCacheAlignment == 0 || (normalizedCapacity & directMemoryCacheAlignmentMask) == 0;

            return normalizedCapacity;
        }

        if (directMemoryCacheAlignment > 0) {
            return alignCapacity(reqCapacity);
        }

        // 如果是16的倍数, 则直接返回
        if ((reqCapacity & 15) == 0) {
            return reqCapacity;
        }
        // 如果不是16的倍数, 则返回 (reqCapacity & ~15) + 16 , 也就是变成最小大于当前值的16的倍数值
        // 其实思路很简单，先把低四位的值抹去（变成0），再加上 16 就得到了目标值。
		// 0000 0000 0000 0000 0000 0000 0010 1100 (44)(原始值)
		// 0000 0000 0000 0000 0000 0000 0000 1111 (15)(15)
		// 1111 1111 1111 1111 1111 1111 1111 0000 (-16)(~15) 				  // ~15
		// 0000 0000 0000 0000 0000 0000 0010 0000 (32)(reqCapacity& ~15) 	  // 抹去低4位
		// 0000 0000 0000 0000 0000 0000 0011 0000 (48)		  			      // +16，补值
        return (reqCapacity & ~15) + 16;
    }

    int alignCapacity(int reqCapacity) {
        int delta = reqCapacity & directMemoryCacheAlignmentMask;
        return delta == 0 ? reqCapacity : reqCapacity + directMemoryCacheAlignment - delta;
    }

    void reallocate(PooledByteBuf<T> buf, int newCapacity, boolean freeOldMemory) {
        assert newCapacity >= 0 && newCapacity <= buf.maxCapacity();

        int oldCapacity = buf.length;
        if (oldCapacity == newCapacity) {
            return;
        }

        PoolChunk<T> oldChunk = buf.chunk;
        ByteBuffer oldNioBuffer = buf.tmpNioBuf;
        long oldHandle = buf.handle;
        T oldMemory = buf.memory;
        int oldOffset = buf.offset;
        int oldMaxLength = buf.maxLength;

        // This does not touch buf's reader/writer indices
        allocate(parent.threadCache(), buf, newCapacity);
        int bytesToCopy;
        if (newCapacity > oldCapacity) {
            bytesToCopy = oldCapacity;
        } else {
            buf.trimIndicesToCapacity(newCapacity);
            bytesToCopy = newCapacity;
        }
        memoryCopy(oldMemory, oldOffset, buf, bytesToCopy);
        if (freeOldMemory) {
            free(oldChunk, oldNioBuffer, oldHandle, oldMaxLength, buf.cache);
        }
    }

    @Override
    public int numThreadCaches() {
        return numThreadCaches.get();
    }

    @Override
    public int numTinySubpages() {
        return tinySubpagePools.length;
    }

    @Override
    public int numSmallSubpages() {
        return smallSubpagePools.length;
    }

    @Override
    public int numChunkLists() {
        return chunkListMetrics.size();
    }

    @Override
    public List<PoolSubpageMetric> tinySubpages() {
        return subPageMetricList(tinySubpagePools);
    }

    @Override
    public List<PoolSubpageMetric> smallSubpages() {
        return subPageMetricList(smallSubpagePools);
    }

    @Override
    public List<PoolChunkListMetric> chunkLists() {
        return chunkListMetrics;
    }

    private static List<PoolSubpageMetric> subPageMetricList(PoolSubpage<?>[] pages) {
        List<PoolSubpageMetric> metrics = new ArrayList<PoolSubpageMetric>();
        for (PoolSubpage<?> head : pages) {
            if (head.next == head) {
                continue;
            }
            PoolSubpage<?> s = head.next;
            for (;;) {
                metrics.add(s);
                s = s.next;
                if (s == head) {
                    break;
                }
            }
        }
        return metrics;
    }

    @Override
    public long numAllocations() {
        final long allocsNormal;
        synchronized (this) {
            allocsNormal = allocationsNormal;
        }
        return allocationsTiny.value() + allocationsSmall.value() + allocsNormal + allocationsHuge.value();
    }

    @Override
    public long numTinyAllocations() {
        return allocationsTiny.value();
    }

    @Override
    public long numSmallAllocations() {
        return allocationsSmall.value();
    }

    @Override
    public synchronized long numNormalAllocations() {
        return allocationsNormal;
    }

    @Override
    public long numDeallocations() {
        final long deallocs;
        synchronized (this) {
            deallocs = deallocationsTiny + deallocationsSmall + deallocationsNormal;
        }
        return deallocs + deallocationsHuge.value();
    }

    @Override
    public synchronized long numTinyDeallocations() {
        return deallocationsTiny;
    }

    @Override
    public synchronized long numSmallDeallocations() {
        return deallocationsSmall;
    }

    @Override
    public synchronized long numNormalDeallocations() {
        return deallocationsNormal;
    }

    @Override
    public long numHugeAllocations() {
        return allocationsHuge.value();
    }

    @Override
    public long numHugeDeallocations() {
        return deallocationsHuge.value();
    }

    @Override
    public  long numActiveAllocations() {
        long val = allocationsTiny.value() + allocationsSmall.value() + allocationsHuge.value()
                - deallocationsHuge.value();
        synchronized (this) {
            val += allocationsNormal - (deallocationsTiny + deallocationsSmall + deallocationsNormal);
        }
        return max(val, 0);
    }

    @Override
    public long numActiveTinyAllocations() {
        return max(numTinyAllocations() - numTinyDeallocations(), 0);
    }

    @Override
    public long numActiveSmallAllocations() {
        return max(numSmallAllocations() - numSmallDeallocations(), 0);
    }

    @Override
    public long numActiveNormalAllocations() {
        final long val;
        synchronized (this) {
            val = allocationsNormal - deallocationsNormal;
        }
        return max(val, 0);
    }

    @Override
    public long numActiveHugeAllocations() {
        return max(numHugeAllocations() - numHugeDeallocations(), 0);
    }

    @Override
    public long numActiveBytes() {
        long val = activeBytesHuge.value();
        synchronized (this) {
            for (int i = 0; i < chunkListMetrics.size(); i++) {
                for (PoolChunkMetric m: chunkListMetrics.get(i)) {
                    val += m.chunkSize();
                }
            }
        }
        return max(0, val);
    }

    protected abstract PoolChunk<T> newChunk(int pageSize, int maxOrder, int pageShifts, int chunkSize);
    protected abstract PoolChunk<T> newUnpooledChunk(int capacity);
    protected abstract PooledByteBuf<T> newByteBuf(int maxCapacity);
    protected abstract void memoryCopy(T src, int srcOffset, PooledByteBuf<T> dst, int length);
    protected abstract void destroyChunk(PoolChunk<T> chunk);

    @Override
    public synchronized String toString() {
        StringBuilder buf = new StringBuilder()
            .append("Chunk(s) at 0~25%:")
            .append(StringUtil.NEWLINE)
            .append(qInit)
            .append(StringUtil.NEWLINE)
            .append("Chunk(s) at 0~50%:")
            .append(StringUtil.NEWLINE)
            .append(q000)
            .append(StringUtil.NEWLINE)
            .append("Chunk(s) at 25~75%:")
            .append(StringUtil.NEWLINE)
            .append(q025)
            .append(StringUtil.NEWLINE)
            .append("Chunk(s) at 50~100%:")
            .append(StringUtil.NEWLINE)
            .append(q050)
            .append(StringUtil.NEWLINE)
            .append("Chunk(s) at 75~100%:")
            .append(StringUtil.NEWLINE)
            .append(q075)
            .append(StringUtil.NEWLINE)
            .append("Chunk(s) at 100%:")
            .append(StringUtil.NEWLINE)
            .append(q100)
            .append(StringUtil.NEWLINE)
            .append("tiny subpages:");
        appendPoolSubPages(buf, tinySubpagePools);
        buf.append(StringUtil.NEWLINE)
           .append("small subpages:");
        appendPoolSubPages(buf, smallSubpagePools);
        buf.append(StringUtil.NEWLINE);

        return buf.toString();
    }

    private static void appendPoolSubPages(StringBuilder buf, PoolSubpage<?>[] subpages) {
        for (int i = 0; i < subpages.length; i ++) {
            PoolSubpage<?> head = subpages[i];
            if (head.next == head) {
                continue;
            }

            buf.append(StringUtil.NEWLINE)
                    .append(i)
                    .append(": ");
            PoolSubpage<?> s = head.next;
            for (;;) {
                buf.append(s);
                s = s.next;
                if (s == head) {
                    break;
                }
            }
        }
    }

    @Override
    protected final void finalize() throws Throwable {
        try {
            super.finalize();
        } finally {
            destroyPoolSubPages(smallSubpagePools);
            destroyPoolSubPages(tinySubpagePools);
            destroyPoolChunkLists(qInit, q000, q025, q050, q075, q100);
        }
    }

    private static void destroyPoolSubPages(PoolSubpage<?>[] pages) {
        for (PoolSubpage<?> page : pages) {
            page.destroy();
        }
    }

    private void destroyPoolChunkLists(PoolChunkList<T>... chunkLists) {
        for (PoolChunkList<T> chunkList: chunkLists) {
            chunkList.destroy(this);
        }
    }

    /**堆内存(heap buffer)
     * @author Ziroom
     *
     */
    static final class HeapArena extends PoolArena<byte[]> {

        HeapArena(PooledByteBufAllocator parent, int pageSize, int maxOrder, int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
            super(parent, pageSize, maxOrder, pageShifts, chunkSize, directMemoryCacheAlignment);
        }

        private static byte[] newByteArray(int size) {
            return PlatformDependent.allocateUninitializedArray(size);
        }

        @Override
        boolean isDirect() {
            return false;
        }

        @Override
        protected PoolChunk<byte[]> newChunk(int pageSize, int maxOrder, int pageShifts, int chunkSize) {
            return new PoolChunk<byte[]>(this, newByteArray(chunkSize), pageSize, maxOrder, pageShifts, chunkSize, 0);
        }

        @Override
        protected PoolChunk<byte[]> newUnpooledChunk(int capacity) {
            return new PoolChunk<byte[]>(this, newByteArray(capacity), capacity, 0);
        }

        @Override
        protected void destroyChunk(PoolChunk<byte[]> chunk) {
            // Rely on GC.
        }

        @Override
        protected PooledByteBuf<byte[]> newByteBuf(int maxCapacity) {
            return HAS_UNSAFE ? PooledUnsafeHeapByteBuf.newUnsafeInstance(maxCapacity)
                    : PooledHeapByteBuf.newInstance(maxCapacity);
        }

        @Override
        protected void memoryCopy(byte[] src, int srcOffset, PooledByteBuf<byte[]> dst, int length) {
            if (length == 0) {
                return;
            }

            System.arraycopy(src, srcOffset, dst.memory, dst.offset, length);
        }
    }

    /**堆外直接内存(direct buffer)
     * @author Ziroom
     *
     */
    static final class DirectArena extends PoolArena<ByteBuffer> {

        DirectArena(PooledByteBufAllocator parent, int pageSize, int maxOrder, int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
            super(parent, pageSize, maxOrder, pageShifts, chunkSize, directMemoryCacheAlignment);
        }

        @Override
        boolean isDirect() {
            return true;
        }

        // mark as package-private, only for unit test
        int offsetCacheLine(ByteBuffer memory) {
            // We can only calculate the offset if Unsafe is present as otherwise directBufferAddress(...) will
            // throw an NPE.
            int remainder = HAS_UNSAFE
                    ? (int) (PlatformDependent.directBufferAddress(memory) & directMemoryCacheAlignmentMask)
                    : 0;

            // offset = alignment - address & (alignment - 1)
            return directMemoryCacheAlignment - remainder;
        }

        @Override
        protected PoolChunk<ByteBuffer> newChunk(int pageSize, int maxOrder, int pageShifts, int chunkSize) {
            if (directMemoryCacheAlignment == 0) {
            	// allocateDirect直接调用jdk申请16m内容空间，见nio--定义《21DirectByteBuffer定义》
            	// 这里直接通过构造函数创建了一个chunk
                return new PoolChunk<ByteBuffer>(this, allocateDirect(chunkSize), pageSize, maxOrder, pageShifts, chunkSize, 0);
            }
            final ByteBuffer memory = allocateDirect(chunkSize + directMemoryCacheAlignment);
            return new PoolChunk<ByteBuffer>(this, memory, pageSize, maxOrder, pageShifts, chunkSize, offsetCacheLine(memory));
        }

        @Override
        protected PoolChunk<ByteBuffer> newUnpooledChunk(int capacity) {
            if (directMemoryCacheAlignment == 0) {
                return new PoolChunk<ByteBuffer>(this, allocateDirect(capacity), capacity, 0);
            }
            final ByteBuffer memory = allocateDirect(capacity + directMemoryCacheAlignment);
            return new PoolChunk<ByteBuffer>(this, memory, capacity, offsetCacheLine(memory));
        }

        private static ByteBuffer allocateDirect(int capacity) {
            return PlatformDependent.useDirectBufferNoCleaner() ?
                    PlatformDependent.allocateDirectNoCleaner(capacity) : ByteBuffer.allocateDirect(capacity);
        }

        @Override
        protected void destroyChunk(PoolChunk<ByteBuffer> chunk) {
            if (PlatformDependent.useDirectBufferNoCleaner()) {
                PlatformDependent.freeDirectNoCleaner(chunk.memory);
            } else {
                PlatformDependent.freeDirectBuffer(chunk.memory);
            }
        }

        @Override
        protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
            if (HAS_UNSAFE) {
                return PooledUnsafeDirectByteBuf.newInstance(maxCapacity);
            } else {
                return PooledDirectByteBuf.newInstance(maxCapacity);
            }
        }

        @Override
        protected void memoryCopy(ByteBuffer src, int srcOffset, PooledByteBuf<ByteBuffer> dstBuf, int length) {
            if (length == 0) {
                return;
            }

            if (HAS_UNSAFE) {
                PlatformDependent.copyMemory(
                        PlatformDependent.directBufferAddress(src) + srcOffset,
                        PlatformDependent.directBufferAddress(dstBuf.memory) + dstBuf.offset, length);
            } else {
                // We must duplicate the NIO buffers because they may be accessed by other Netty buffers.
                src = src.duplicate();
                ByteBuffer dst = dstBuf.internalNioBuffer();
                src.position(srcOffset).limit(srcOffset + length);
                dst.position(dstBuf.offset);
                dst.put(src);
            }
        }
    }
}
